package jrain.fw.business.common;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;

import jrain.fw.core.business.Business;
import jrain.fw.core.business.BusinessConst;
import jrain.fw.core.business.BusinessInfo;
import jrain.fw.core.business.BusinessInterceptor;
import jrain.fw.core.business.BusinessMethod;
import jrain.fw.core.business.BusinessProvider;
import jrain.fw.core.business.BusinessRequest;
import jrain.fw.core.business.BusinessResponse;
import jrain.fw.core.business.Interceptor;
import jrain.fw.core.consts.Consts;
import jrain.fw.core.log.Logger;
import jrain.fw.core.log.LoggerFactory;
import jrain.fw.core.service.DestroyService;
import jrain.fw.core.service.InitService;
import jrain.fw.core.service.Service;
import jrain.fw.core.service.ServiceConst;
import jrain.fw.core.utils.FwUtils;
import jrain.utils.cls.ClassUtils;
import jrain.utils.collection.CollectionUtils;
import jrain.utils.exception.BusinessException;
import jrain.utils.exception.ServiceException;
import jrain.utils.lang.LangUtils;
import jrain.utils.lang.StringUtils;

@Service(group = ServiceConst.S_BUSINESS_PROVIDER, module = ServiceConst.M_BUSINESS)
public class BusinessProviderService implements BusinessProvider, InitService, DestroyService {

	private static final Logger log = LoggerFactory.getLogger(BusinessProviderService.class);

	private static final Map<String, BusinessBean> businessMap = new HashMap<>();
	private static final Map<String, BusinessInfo> businessInfoMap = new HashMap<>();
	private static final Map<String, BusinessInterceptor> interceptorMap = new HashMap<>();
	private static final AtomicBoolean init = new AtomicBoolean(false);
	private final List<String> interceptors;

	public BusinessProviderService() {
		String name = Consts.BUSINESS_INTERCEPTOR;
		String value =Consts.BUSINESS_INTERCEPTOR_DVALUE;
		String interceptorsStr = StringUtils.trimNull(FwUtils.getFwCfg().get(name),value);
		interceptors = CollectionUtils.valueOfList(interceptorsStr);
	}

	@Override
	public BusinessResponse doSync(BusinessRequest request) {
		BusinessResponse businessResponse = new BusinessResponse();
		if (init.get()) {
			BusinessBean businessBean = businessMap.get(request.getKey());
			BusinessInfo businessInfo = businessInfoMap.get(request.getKey());
			boolean rest = LangUtils.parseBoolean(request.getExtData(BusinessConst.EXT_IS_REST));
			if (businessBean != null && businessInfo != null && (!rest || (rest && !businessInfo.isInner()))) {
				try {
					// 执行拦截器
					if (interceptors != null) {
						for (String interceptor : interceptors) {
							BusinessInterceptor businessInterceptor = getInterceptor(interceptor);
							if (businessInterceptor != null) {
								BusinessResponse tempResponse = businessInterceptor.handler(request);
								if (tempResponse.getCode() != BusinessConst.RES_CODE_SUCCESS) {
									return tempResponse;
								}
							}
						}
					}
					businessResponse.setCode(BusinessConst.RES_CODE_ERROR_BUIS_NOTFIND);
					businessResponse = (BusinessResponse) businessBean.getMethod().invoke(businessBean.getObj(), request);
				} catch (InvocationTargetException t) {
					Throwable tt = t.getTargetException();
					if (tt instanceof BusinessException) {
						BusinessException e = (BusinessException) tt;
						businessResponse.setCode(e.getCode());
						businessResponse.setMsg(e.getMessage());
						log.error(request, e.getMessage(), e);
					} else if (tt instanceof ServiceException) {
						ServiceException e = (ServiceException) tt;
						businessResponse.setCode(e.getCode());
						businessResponse.setMsg(e.getMessage());
						log.error(request, e.getMessage(), e);
					} else {
						businessResponse.setCode(BusinessConst.RES_CODE_ERROR_UNKOWN);
						businessResponse.setMsg(t.getMessage());
						log.error(request, t.getMessage(), t);
					}
				} catch (BusinessException e) {
					businessResponse.setCode(e.getCode());
					businessResponse.setMsg(e.getMessage());
					log.error(request, e.getMessage(), e);
				} catch (ServiceException e) {
					businessResponse.setCode(e.getCode());
					businessResponse.setMsg(e.getMessage());
					log.error(request, e.getMessage(), e);
				} catch (Throwable e) {
					businessResponse.setCode(BusinessConst.RES_CODE_ERROR_UNKOWN);
					businessResponse.setMsg(e.getMessage());
					log.error(request, e.getMessage(), e);
				}
			} else {
				businessResponse.setCode(BusinessConst.RES_CODE_ERROR_BUIS_NOTFIND);
				log.warn(request, " business not found!");
			}
			return businessResponse;
		} else {
			log.warn(request, "{} business not found!,service not initialized!");
			businessResponse.setCode(BusinessConst.RES_CODE_ERROR_BUIS_NOTFIND);
			return businessResponse;
		}
	}

	@Override
	public void doAsync(BusinessRequest request, Consumer<BusinessResponse> callbak) {
		BusinessResponse businessResponse = new BusinessResponse();
		String key = request.getKey();
		if (init.get()) {
			BusinessBean businessBean = businessMap.get(key);
			BusinessInfo businessInfo = businessInfoMap.get(request.getKey());
			boolean rest = LangUtils.parseBoolean(request.getExtData(BusinessConst.EXT_IS_REST));
			if (businessBean != null && businessInfo != null && (!rest || (rest && !businessInfo.isInner()))) {
				try {
					// 执行拦截器
					if (interceptors != null) {
						for (String interceptor : interceptors) {
							BusinessInterceptor businessInterceptor = getInterceptor(interceptor);
							if (businessInterceptor != null) {
								BusinessResponse tempResponse = businessInterceptor.handler(request);
								if (tempResponse.getCode() != BusinessConst.RES_CODE_SUCCESS) {
									callbak.accept(tempResponse);
									return;
								}
							}
						}
					}
					businessResponse.setCode(BusinessConst.RES_CODE_ERROR_BUIS_NOTFIND);
					businessBean.getMethod().invoke(businessBean.getObj(), request, callbak);
				} catch (InvocationTargetException t) {
					Throwable tt = t.getTargetException();
					if (tt instanceof BusinessException) {
						BusinessException e = (BusinessException) tt;
						businessResponse.setCode(e.getCode());
						businessResponse.setMsg(e.getMessage());
						log.error(request, e.getMessage(), e);
					} else if (tt instanceof ServiceException) {
						ServiceException e = (ServiceException) tt;
						businessResponse.setCode(e.getCode());
						businessResponse.setMsg(e.getMessage());
						log.error(request, e.getMessage(), e);
					} else {
						businessResponse.setCode(BusinessConst.RES_CODE_ERROR_UNKOWN);
						businessResponse.setMsg(t.getMessage());
						log.error(request, t.getMessage(), t);
					}
				} catch (BusinessException e) {
					businessResponse.setCode(e.getCode());
					businessResponse.setMsg(e.getMessage());
					log.error(request, e.getMessage(), e);
					callbak.accept(businessResponse);
				} catch (ServiceException e) {
					businessResponse.setCode(e.getCode());
					businessResponse.setMsg(e.getMessage());
					log.error(request, e.getMessage(), e);
					callbak.accept(businessResponse);
				} catch (Throwable t) {
					businessResponse.setCode(BusinessConst.RES_CODE_ERROR_UNKOWN);
					businessResponse.setMsg(t.getMessage());
					log.error(request, t.getMessage(), t);
					callbak.accept(businessResponse);
				}
			} else {
				businessResponse.setCode(BusinessConst.RES_CODE_ERROR_BUIS_NOTFIND);
				log.warn(request, " business not found!");
				callbak.accept(businessResponse);
			}
		} else {
			log.warn(request, " business not found!,service not initialized!");
			businessResponse.setCode(BusinessConst.RES_CODE_ERROR_BUIS_NOTFIND);
			callbak.accept(businessResponse);
		}
	}

	@Override
	public Collection<BusinessInfo> listBusinessInfos() {
		return businessInfoMap.values();
	}

	@Override
	public BusinessInfo getBusinessInfo(String key) {
		return businessInfoMap.get(key);
	}

	@Override
	public BusinessInterceptor getInterceptor(String name) {
		return interceptorMap.get(name);
	}

	@Override
	public Collection<BusinessInterceptor> listInterceptor() {
		return interceptorMap.values();
	}

	@Override
	public void init() {
		if (!init.getAndSet(true)) {
			BusinessRequest request = BusinessConst.LOG_BOOT_REQUEST;
			try {
				String pkgs = StringUtils.trimNull(FwUtils.getFwCfg().get(Consts.FW_SCAN_PKGS),Consts.CFG_SCAN_PKGS_DVALUE);
				List<String> pkgList = CollectionUtils.valueOfList(pkgs);
				log.info(request, "business scan pkgs {}", pkgs);
				for (String string : pkgList) {
					Set<Class<?>> list = ClassUtils.getClasses(string);
					for (Class<?> claz : list) {
						Business service = claz.getAnnotation(Business.class);
						if (service != null) {
							Method[] methods = claz.getDeclaredMethods();
							for (Method method : methods) {
								BusinessMethod businessMethod = method.getAnnotation(BusinessMethod.class);
								if (businessMethod != null) {
									String key = service.name() + "." + method.getName() + "." + businessMethod.version();
									log.info(request, "business  name:{} class:{}", key, claz.getName());
									businessMap.put(key, new BusinessBean(claz.newInstance(), method));
									BusinessInfo bean = new BusinessInfo();
									bean.setService(service.name());
									bean.setMethod(method.getName());
									bean.setService(businessMethod.version());
									bean.setRetry(businessMethod.retry());
									businessInfoMap.put(key, bean);
								}
							}
						}
						// 扫描拦截器
						Interceptor interceptor = claz.getAnnotation(Interceptor.class);
						if (interceptor != null) {
							Object bean = claz.newInstance();
							if (bean instanceof BusinessInterceptor) {
								String name = interceptor.name();
								interceptorMap.put(name, (BusinessInterceptor) bean);
								log.info(request, "business interceptor  name:{} class:{}", name, claz.getName());
							}
						}
					}
				}
				log.info(request, "{} started!", ServiceConst.S_BUSINESS_PROVIDER);
			} catch (Exception e) {
				String msg = ServiceConst.S_BUSINESS_PROVIDER + "start error!";
				log.error(request, msg, e);
				throw new ServiceException(msg, e);
			}
		}
	}

	public void destroy() {
		if (init.get()) {
			businessMap.clear();
			businessInfoMap.clear();
			log.info(BusinessConst.LOG_BOOT_REQUEST, "{} stoped!", ServiceConst.S_BUSINESS_PROVIDER);
			init.set(false);
		}
	}

}
